﻿using AutoMapper;
using Castle.MicroKernel.Registration;
using System;
using System.Reflection;
using Yto.AutoMapper.Attributes;
using Yto.AutoMapper.Extensions;
using Yto.Common.Dependency;
using Yto.Modules;
using Yto.Reflection;

namespace Yto.AutoMapper
{
    [Depends(typeof(KernelModule))]
    public class AutoMapperModule : BaseModule
    {
        //private static readonly object SyncObj = new object();
        private readonly ITypeFinder _typeFinder;

        public AutoMapperModule(ITypeFinder typeFinder)
        {
            _typeFinder = typeFinder;
        }

        public override void BeforeInitialize()
        {
            CtnManager.Regist<IAutoMapperConfiguration, AutoMapperConfiguration>();
        }

        public override void AfterInitialize()
        {
            CreateMappings();
        }

        private void CreateMappings()
        {
            //lock (SyncObj)
            //{ Mapper.Initialize(actionConfiguration); static method will duplicate mapping in an application.
            Action<IMapperConfigurationExpression> actionConfiguration = con =>
            {
                FindMapTypes(con);
                foreach (var item in Configuration.MapperConfiguration().ConfigActions)
                {
                    item(con);
                }
            };

            var config = new MapperConfiguration(actionConfiguration);
            CtnManager.Container.Register(
                Component.For<IConfigurationProvider>().Instance(config).LifestyleSingleton()
                );
            CtnManager.Container.Register(
                Component.For<IMapper>().Instance(config.CreateMapper()).LifestyleSingleton()
                );

            //}
        }

        private void FindMapTypes(IMapperConfigurationExpression con)
        {
            var types = _typeFinder.FindByCondition(type =>
            {
                var typeInfos = type.GetTypeInfo();
                return typeInfos.IsDefined(typeof(AutoMapBothAttribute)) ||
                        typeInfos.IsDefined(typeof(AutoMapFromAttribute)) ||
                        typeInfos.IsDefined(typeof(AutoMapToAttribute));
            });

            Logger.DebugFormat($"Find {types.Length} classes defind autoMapAtrribute!");

            foreach (var item in types)
            {
                Logger.Debug(item.FullName);
                con.CreateAutoAttributeMaps(item);
            }
        }
    }
}
